<?php
/**
 * Box2 Plugin: Draw highlighting boxes around wiki markup
 *
 * Syntax:     <box2 width% Ncol classes>
 *   width%    width of the box, must use % unit
 *   classes   one or more classes used to style the box, several predefined styles included in style.css
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Christopher Smith <chris@jalakai.co.uk>  
 * @author     Stephane Chamberland <stephane.chamberland@gmail.com>
 */

if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');

/**
 * All DokuWiki plugins to extend the parser/rendering mechanism
 * need to inherit from this class
 */
class syntax_plugin_box2 extends DokuWiki_Syntax_Plugin {

    var $colnb = 1;
    var $maxcol = 1;
    var $mystyle = array();
   
    /**
     * return some info
     */
    function getInfo(){
      return array(
        'author' => 'Stephane Chamberland',
        'email'  => 'stephane.chamberland@gmail.com',
        'date'   => '2007-07-01',
        'name'   => 'Box2 Plugin',
        'desc'   => 'Boxes with titles, colour, simple or double. 
                     Syntax: <box2 Ncol class> ... </box2> 
										 class is optional.
										 the box contents can include almost any wiki markup.',
        'url'    => 'http://wiki.splitbrain.org/plugin:boxes',
      );
    }

    function getType(){ return 'protected';}
    function getAllowedTypes() { return array('container','substition','protected','disabled','formatting','paragraphs'); }
    function getPType(){ return 'block';}

    // must return a number lower than returned by native 'code' mode (200)
    function getSort(){ return 197; }

    // override default accepts() method to allow nesting 
    // - ie, to get the plugin accepts its own entry syntax
//   function accepts($mode) {
//       if ($mode == substr(get_class($this), 7)) return true;
//        return parent::accepts($mode);
//   }

    /**
     * Connect pattern to lexer
     */
    function connectTo($mode) {       
      $this->Lexer->addEntryPattern('<box2>(?=.*?</box2>)',$mode,'plugin_box2');
      $this->Lexer->addEntryPattern('<box2\s[^\r\n]*?>(?=.*?</box.*?>)',$mode,'plugin_box2');
    }

    function postConnect() {
        $this->Lexer->addPattern('<box2_sep\s*?/>', 'plugin_box2');
        $this->Lexer->addPattern('<box2_sep\s[^\r\n]*?/>', 'plugin_box2');
        $this->Lexer->addPattern('<h[0-6]>', 'plugin_box2');
        $this->Lexer->addPattern('</h[0-6]>', 'plugin_box2');
        $this->Lexer->addExitPattern('</box2>', 'plugin_box2');
    }

    /**
     * Handle the match
     */
    function handle($match, $state, $pos, &$handler){

        switch ($state) {
            case DOKU_LEXER_ENTER:
                $this->mystyle = $this->_boxstyle(trim(substr($match, 5, -1)));
                if (isset($this->mystyle['maxcol'])) {
                    if ($this->mystyle['maxcol']== "2col") {
                        $this->maxcol = 2;
                    } else if ($this->mystyle['maxcol'] != "1col") {
                        $this->maxcol = 3;
                    } else {
						$this->mystyle['maxcol'] = "1col";
                        $this->maxcol = 1;
                    }
                } else {
					$this->mystyle['maxcol'] = "1col";
                    $this->maxcol = 1;
                }
                if (!isset($this->mystyle['class'])) $this->mystyle['class'] = "";
                if (!isset($this->mystyle['class'])) $this->mystyle['class0'] = "";
                $this->colnb = 1;
                return array('box2_open',$this->mystyle);

            case DOKU_LEXER_MATCHED:
				if (trim(substr($match, 0, 2)) == "<h") {
					return array('box2_hh',$match);
				} else if (trim(substr($match, 0, 3)) == "</h") {
					return array('box2_hh',$match);
				} else {
					$mystyle2 = $this->_boxstyle(trim(substr($match, 9, -2)));
					if (!isset($mystyle2['class'])) $mystyle2['class'] = $this->mystyle['class'];
					if (!isset($mystyle2['class0'])) $mystyle2['class'] = $this->mystyle['class0'];
					$this->colnb++;
					$mystyle2['maxcol'] = $this->mystyle['maxcol'];
					$mystyle2['newrow'] = false;
					if ($this->colnb > $this->maxcol) {
						$this->colnb = 1;
						$mystyle2['newrow'] = true;
					}
					return array('box2_sep',$mystyle2);
				}
 
            case DOKU_LEXER_UNMATCHED:                
                return array('data', $match);

            case DOKU_LEXER_EXIT:
                return array('box2_close', '');

        }       
        return false;
    }

    /**
     * Create output
     */
    function render($mode, &$renderer, $indata) {

      list($instr, $data) = $indata;
     
		$boxopen = "<div class='box2 box2_".$data['maxcol']." ".$data['class0']."'>";
		$boxclose = "</div><div class='clearer'></div>";
		$iboxopen = "<div class='box2i'><div class='box2c ".$data['class']."'>";
		$i2boxopen = "<div class='box2i2'><div class='box2c ".$data['class']."'>";
		$iboxclose = "</div></div>";
	
      if($mode == 'xhtml'){
          switch ($instr) {
     
          case 'box2_open' :
			$renderer->doc .= $boxopen.$iboxopen;
           break;
     
          case 'box2_hh' :   
            $renderer->doc .= $data;
            break;
     
          case 'data' :      
            $renderer->doc .= $renderer->_xmlEntities($data); 
            break;
            
         case 'box2_sep' :
            if ($data['newrow']) {
                $renderer->doc .= $iboxclose.$boxclose.$boxopen.$iboxopen;
            } else {
                $renderer->doc .= $iboxclose.$i2boxopen;
            }
            break;

          case 'box2_close' : 
            $renderer->doc .= $iboxclose.$boxclose;
            break;
        }

        return true;
      }
      return false;
    }
	
	function _stripclass($myclass) {
		$myclassarray = explode(' ',$myclass);
		$myclass2 = "";
		foreach($myclassarray as $myitem) {
			if (substr($myitem,0,2) != 'i_') $myclass2 .= " ".$myitem;
		}
		return trim($myclass2);
	}
	
    function _boxstyle($str) {
      if (!strlen($str)) return array();

      $styles = array();

      $tokens = preg_split('/\s+/', $str, 9);                      // limit is defensive
      foreach ($tokens as $token) {
            if (preg_match('/^\d+col$/', $token)) {
                    $styles['maxcol'] = $token;
                    continue;
            }
         if (preg_match('/^\d*\.?\d+(%|px|em|ex|pt|cm|mm|pi|in)$/', $token)) {
            $styles['width'] = $token;
            continue;
          }
 
          // restrict token (class names) characters to prevent any malicious data
          if (preg_match('/[^A-Za-z0-9_-]/',$token)) continue;
          $styles['class'] = (isset($styles['class']) ? $styles['class'].' ' : '').$token;
		  $styles['class0'] = $this->_stripclass($styles['class']);
      }

      return $styles;
    }

}

//Setup VIM: ex: et ts=4 enc=utf-8 :